#include "depui/depui.h"

#ifdef MX_PLATFORM_GENERIC

#define MXMODULE_DRS

#define GRX_SKIP_INLINES
#include <mgrx.h>
#include <mgrxkeys.h>

static MX_RECT mx__mgrx_screen;
static GrEvent mx__grx_event;
static GrTextOption mx__textoption;
static GrColor *mx__egacolors = 0;
static int mx__grx_pressed = 0;
static int mx__grx_init = 0;

#define BLUE         mx__egacolors[1]
#define GREEN        mx__egacolors[2]
#define CYAN         mx__egacolors[3]
#define RED          mx__egacolors[4]
#define MAGENTA      mx__egacolors[5]
#define BROWN        mx__egacolors[6]
#define LIGHTBLUE    mx__egacolors[9]
#define LIGHTGREEN   mx__egacolors[10]
#define LIGHTCYAN    mx__egacolors[11]
#define LIGHTRED     mx__egacolors[12]
#define LIGHTMAGENTA mx__egacolors[13]
#define YELLOW       mx__egacolors[14]

static MX_PLATFORM_FONT mx__mgrx_font[] = {
	{&GrFont_PC6x8, "Builtin 6x8"},
	{&GrFont_PC8x8, "Builtin 8x8"},
	{&GrFont_PC8x14, "Builtin 8x14"},
	{&GrFont_PC8x16, "Builtin 8x16"},
};

#define MX__ARRAY(a) (sizeof(a)/sizeof(a[0]))

const MX_PLATFORM_FONT *mx_platform_font_system(const int i)
{
	if (i < 0)
		return &mx__mgrx_font[1];

	if (i < (int) MX__ARRAY(mx__mgrx_font))
		return &mx__mgrx_font[i];

	return 0;
}

void *mx_platform_font_load(const char *filename)
{
	return GrLoadFont((char *) filename);
}

const char *mx_platform_font_path(void)
{
	return "*.fnt";
}

void mx_platform_font_free(void *font)
{
	GrFont *f = (GrFont *) font;

	GrUnloadFont(f);
}

unsigned int mx_platform_font_height(void *font)
{
	if (!font)
		font = &GrDefaultFont;

	mx__textoption.txo_font = (GrFont *) font;

	return GrStringHeight("HI", 2, &mx__textoption);
}

unsigned int mx_platform_font_width(void *font, const char *text, int len)
{
	if (!font)
		font = &GrDefaultFont;

	mx__textoption.txo_font = (GrFont *) font;

	if (len < 0)
		len = strlen(text);

	return GrStringWidth((void *) text, len, &mx__textoption);
}

static const MX_PLATFORM_RESOLUTION mx__mgrx_resolutions[] = {
	{640, 480, "640x480"},
	{800, 600, "800x600"},
	{1024, 768, "1024x768"},
	{1280, 1024, "1280x1024"},
	{0, 0, 0}
};

static const MX_PLATFORM_DEPTH mx__mgrx_depths[] = {
	{4, "4"},
	{8, "8"},
	{16, "16"},
	{24, "24"},
	{32, "32"},
	{0, 0}
};

static const MX_PLATFORM_DRIVER mx__mgrx_drivers[] = {
	{"VESA gw 640 gh 480 nc 64K", "Vesa"},
	{"stdvga gw 640 gh 480 nc 64K", "Vga"},
	{"stdega gw 640 gh 480 nc 64K", "Ega"},
	{0, 0}
};

extern const MX_THEME mx_theme_mgrx;

static MX_PLATFORM_THEME mx__mgrx_themes[] = {
	{&mx_theme_mgrx, "mgrx theme"},
	{0, 0},
};

void mx_platform_modes(const MX_PLATFORM_DRIVER * d[],
					   const MX_PLATFORM_RESOLUTION * r[],
					   const MX_PLATFORM_DEPTH * c[],
					   const MX_PLATFORM_THEME * t[])
{
	*d = mx__mgrx_drivers;
	*r = mx__mgrx_resolutions;
	*c = mx__mgrx_depths;
	*t = mx__mgrx_themes;
}

unsigned mx_platform_start(int w, int h, int c, const void *driver)
{
	(void) driver;

	if (w == 0)
		w = 640;
	if (h == 0)
		h = 480;
	if (c == 0)
		c = 16;

	if (mx__grx_init) {
		GrMouseEraseCursor();
		GrEventUnInit();
	}

	GrSetDriver((char *) driver);

	if (!GrSetMode(GR_width_height_bpp_graphics, w, h, c))
		return false;

	if (!GrMouseDetect())
		return false;

	mx__egacolors = GrAllocEgaColors();
	GrEventInit();
	GrEventGenMmove(GR_GEN_MMOVE_ALWAYS);
	mx__grx_init = 1;

	GrMouseDisplayCursor();

	mx__mgrx_screen.x1 = 0;
	mx__mgrx_screen.y1 = 0;
	mx__mgrx_screen.x2 = GrMaxX();
	mx__mgrx_screen.y2 = GrMaxY();

	mx__textoption.txo_font = &GrDefaultFont;
	mx__textoption.txo_fgcolor.v = GrNOCOLOR;
	mx__textoption.txo_bgcolor.v = GrNOCOLOR;
	mx__textoption.txo_chrtype = GR_BYTE_TEXT;
	mx__textoption.txo_direct = GR_TEXT_RIGHT;
	mx__textoption.txo_xalign = GR_ALIGN_LEFT;
	mx__textoption.txo_yalign = GR_ALIGN_TOP;

	mx_drs_area(mx__mgrx_screen.x2, mx__mgrx_screen.y2);

	return true;
}

void mx_platform_stop(void)
{
	GrEventUnInit();
	GrMouseEraseCursor();
	GrSetMode(GR_default_text);

	mx__grx_init = 0;
}

unsigned mx_platform_clip(const MX_RECT * r)
{
	GrSetClipBox(r->x1, r->y1, r->x2, r->y2);
	return true;
}

const MX_RECT *mx_platform_rect(void)
{
	mx__mgrx_screen.x2 = GrMaxX();
	mx__mgrx_screen.y2 = GrMaxY();

	return &mx__mgrx_screen;
}

static void mx__mgrx_flush(MX_RECT * rect)
{
	GrMouseEraseCursor();

	mx__gui_redraw(rect);

	GrMouseDisplayCursor();
}

unsigned mx_platform_poll(void)
{
	GrEventRead(&mx__grx_event);

	mx_drs_update(mx__mgrx_flush);

	return true;
}

void mx_platform_dirty(const MX_RECT * rect)
{
	mx_drs_dirty(rect, true);
}

unsigned mx_platform_pointer(int *px, int *py, int *pb)
{
	switch (mx__grx_event.type) {

	case GREV_MOUSE:
		*px = mx__grx_event.p2;
		*py = mx__grx_event.p3;

		if (mx__grx_event.p1 == GRMOUSE_LB_PRESSED)
			mx__grx_pressed = 1;
		else if (mx__grx_event.p1 == GRMOUSE_LB_RELEASED)
			mx__grx_pressed = 0;
		*pb = mx__grx_pressed;

		break;

	case GREV_MMOVE:
		*px = mx__grx_event.p2;
		*py = mx__grx_event.p3;
		mx__grx_pressed = (mx__grx_event.p1 == 1) ? 1 : 0;
		*pb = mx__grx_pressed;
		break;

	default:
		return false;
	}
	mx__grx_event.type = GREV_NULL;
	return true;
}

unsigned mx_platform_key(int *scan, int *ascii)
{
	if (mx__grx_event.type != GREV_KEY)
		return false;

	*scan = 0;
	*ascii = 0;

	if (mx__grx_event.p1 & 0xff00)
		*scan = mx__grx_event.p1;
	else
		*ascii = mx__grx_event.p1;

	mx__grx_event.type = GREV_NULL;
	return true;
}

static unsigned mx_theme_mgrx_start(void)
{
	return true;
}

static void mx_theme_mgrx_stop(void)
{
}

static void mx_theme_mgrx_event(void)
{
	mx__event_default();
}

#define HLINE(x1,y1,x2,c) GrHLine(x1, x2, y1, c)
#define VLINE(x1,y1,y2,c) GrVLine(x1, y1, y2, c)
#define RFILL(x1,y1,x2,y2,c) GrFilledBox(x1, y1, x2, y2, c)

#define WHITE        mx__egacolors[15]
#define DARKGRAY     mx__egacolors[8]
#define LIGHTGRAY    mx__egacolors[7]
#define BLACK        mx__egacolors[0]

static void mx__mgrx_frame(const MX_RECT * objrect, int edge, int fill)
{
	HLINE(objrect->x1, objrect->y1, objrect->x2, edge);
	HLINE(objrect->x1, objrect->y2, objrect->x2, edge);
	VLINE(objrect->x1, objrect->y1 + 1, objrect->y2 - 1, edge);
	VLINE(objrect->x2, objrect->y1 + 1, objrect->y2 - 1, edge);

	RFILL(objrect->x1 + 1, objrect->y1 + 1, objrect->x2 - 1, objrect->y2 - 1,
		  fill);
}

static void mx_theme_mgrx_obj(MX_OBJ * obj)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(obj);

		RFILL(objrect->x1, objrect->y1, objrect->x2, objrect->y2, WHITE);
	}
}

static void mx_theme_mgrx_vslider(MX_SLIDER * slider)
{
	if (mx_exposing()) {
		int fill = WHITE;
		MX_RECT objrect = *MXRECT(slider);

		mx__mgrx_frame(&objrect, DARKGRAY, LIGHTGRAY);

		if (mx_armed(slider))
			fill = GREEN;

		objrect.y2 = objrect.y1 + slider->_lower;
		objrect.y1 += slider->_upper;

		mx__mgrx_frame(&objrect, BLACK, fill);
	}
}

static void mx_theme_mgrx_hslider(MX_SLIDER * slider)
{
	if (mx_exposing()) {
		int fill = WHITE;
		MX_RECT objrect = *MXRECT(slider);

		mx__mgrx_frame(&objrect, DARKGRAY, LIGHTGRAY);

		if (mx_armed(slider))
			fill = GREEN;

		objrect.x2 = objrect.x1 + slider->_lower;
		objrect.x1 += slider->_upper;

		mx__mgrx_frame(&objrect, BLACK, fill);
	}
}

static void mx__textchunks(const MX_TEXTUAL * textual, int x1, int y1, int x2,
						   int y2, int offsetx, int offsety, GrColor fore)
{
	MX__TEXTCHUNK *chunk = textual->_first;
	void *font = mx__textual_platform_font(textual);

	if (!font)
		font = &GrDefaultFont;

	while (chunk) {
		int len = 0;
		const char *text = mx_string_text(&chunk->text, &len);

		mx__textual_chunkalign(textual, x2 - x1, y2 - y1, chunk);

		if (chunk->cursor) {
			GrColor fill = LIGHTGRAY;

			if (mx_focused(textual))
				fill = GREEN;

			GrFilledBox(x1 + chunk->rect.x1 + chunk->alignx,
						y1 + chunk->rect.y1 + chunk->aligny,
						x1 + chunk->rect.x2 + chunk->alignx,
						y1 + chunk->rect.y2 + chunk->aligny, fill);
		}

		if (text) {
			if (len < 0)
				len = strlen(text);

			mx__textoption.txo_font = (GrFont *) font;
			mx__textoption.txo_fgcolor.v = fore;

			GrDrawString((char *) text, len,
						 x1 + chunk->rect.x1 + chunk->alignx + offsetx,
						 y1 + chunk->rect.y1 + chunk->aligny + offsety,
						 &mx__textoption);
		}

		chunk = chunk->_next;
	}
}

static int mx__textback(const MX_TEXTUAL * textual, int x1, int y1, int x2,
						int y2, unsigned armable)
{
	int fore = BLACK;
	int back = WHITE;

	if (mx_selected(textual)) {
		back = GREEN;
		fore = WHITE;
	}

	if (mx_disabled(textual))
		fore = DARKGRAY;

	if ((armable) && (mx_armed(textual)))
		back = LIGHTGRAY;

	GrFilledBox(x1, y1, x2, y2, back);

	return fore;
}

static void mx_theme_mgrx_textual(MX_TEXTUAL * text)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(text);
		int fore = mx__textback(text, objrect->x1, objrect->y1,
								objrect->x2, objrect->y2, false);

		mx__textchunks(text, objrect->x1, objrect->y1, objrect->x2, objrect->y2,
					   0, 0, fore);
	}
}

static void mx_theme_mgrx_scrollcorner(MX_OBJ * scrollcorner)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(scrollcorner);

		GrFilledBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2,
					LIGHTGRAY);
	}
}

static void mx_theme_mgrx_scroll(MX_SCROLL * scroll)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(scroll);

		GrFilledBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2, WHITE);
	}
}

static void mx_theme_mgrx_button(MX_BUTTON * button)
{
	if (mx_exposing()) {
		int fore;
		const MX_RECT *objrect = MXRECT(button);
		int offset = 0;

		if (mx_selected(button))
			offset = 1;

		if (mx_armed(button))
			GrBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2, RED);

		else if (mx_selected(button))
			GrBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2, WHITE);

		else
			GrBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2, DARKGRAY);

		fore =
			mx__textback(MXTEXTUAL(button), objrect->x1 + 1, objrect->y1 + 1,
						 objrect->x2 - 1, objrect->y2 - 1, true);

		mx__textchunks(MXTEXTUAL(button), objrect->x1 + 1 + offset,
					   objrect->y1 + 1 + offset, objrect->x2 - 1,
					   objrect->y2 - 1, offset, offset, fore);
	}
}

void mx_theme_mgrx_listelem(MX_LISTELEM * listelem)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(listelem);

		int fore = mx__textback(MXTEXTUAL(listelem), objrect->x1, objrect->y1,
								objrect->x2, objrect->y2, true);

		mx__textchunks(MXTEXTUAL(listelem), objrect->x1, objrect->y1,
					   objrect->x2, objrect->y2, 0, 0, fore);
	}
}

static void mx_theme_mgrx_root(MX_WIN * root)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(root);

		GrFilledBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2,
					LIGHTGRAY);
	}
}

static void mx_theme_mgrx_winborder(MX_WINBORDER * border)
{
	if (mx_exposing()) {
		MX_WIN *owner = MXOBJ(border)->_win;
		const MX_RECT *objrect = MXRECT(border);
		const int x1 = objrect->x1;
		const int y1 = objrect->y1;
		const int x2 = objrect->x2;
		const int y2 = objrect->y2;
		int fore = BLACK;
		int fill = WHITE;

		if (mx_win_active(owner))
			fill = GREEN;

		if (mx_disabled(owner))
			fore = LIGHTGRAY;

		mx__mgrx_frame(objrect, BLACK, fill);

		if (owner)
			mx__textchunks(MXTEXTUAL(owner), x1 + 2, y1 + 2, x2 - 2, y2 - 2, 0,
						   0, fore);
	}
}

static void mx_theme_mgrx_win(MX_WIN * win)
{
	if (mx_exposing()) {
		const MX_RECT *objrect = MXRECT(win);

		GrFilledBox(objrect->x1, objrect->y1, objrect->x2, objrect->y2,
					LIGHTGRAY);
	}
}

const MX_THEME mx_theme_mgrx = {
	mx_theme_mgrx_start,
	mx_theme_mgrx_stop,
	mx_theme_mgrx_event,
	mx_theme_mgrx_obj,
	mx_theme_mgrx_vslider,
	mx_theme_mgrx_hslider,
	mx_theme_mgrx_textual,
	mx_theme_mgrx_scrollcorner,
	mx_theme_mgrx_scroll,
	mx_theme_mgrx_button,
	mx_theme_mgrx_listelem,
	mx_theme_mgrx_root,
	mx_theme_mgrx_winborder,
	mx_theme_mgrx_win
};

#endif
